/**
 * Procesadores de Lenguaje, curso 2009-2010
 * @author grupo 02
 *
 */

package interprete;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.HashMap;


public class MaquinaP {


    //codigos de tipos de operaciones
    private final int OPPILA       =  0;
    private final int OPARIT       =  16;
    private final int OPLOG        =  32;
    private final int OPMISC       =  48;

    //operaciones pila-memoria "00xxxx"
    //codigo aplicada la mascara "001111"
    private final int APILA        = 0;
    private final int APILA_DIR    = 1;
    private final int DESAPILA_DIR = 2;


    //operaciones aritmeticas "01xxxx"
    //codigo aplicada la mascara "001111"

    private final int SUMA  = 0;
    private final int RESTA = 1;
    private final int MULT  = 2;
    private final int DIV   = 3;
    private final int MOD   = 4;
    private final int DESPD = 5;
    private final int DESPI = 6;
    private final int SIGNO = 7;
    private final int ABS   = 8;

    //operaciones logicas "10xxxx"
    //codigo aplicada la mascara "001111"

    private final int AND   = 0;
    private final int OR    = 1;
    private final int MENOR = 2;
    private final int MAYOR = 3;
    private final int MENIG = 4;
    private final int MAYIG = 5;
    private final int DIST  = 6;
    private final int IGUAL = 7;
    private final int NEG   = 8;

    //otras operaciones "11xxxx"
    //codigo aplicada la mascara "001111"

    private final int CFLOAT = 0;
    private final int CINT   = 1;
    private final int CNAT   = 2;
    private final int CCHAR  = 3;
    private final int LEER   = 4;
    private final int ESCR   = 5;
    private final int STOP   = 15;



    private class Instruction {

        String code;
        String field;

        private String translation(String code){
            int op_type = (Integer.parseInt(code,2) & 48);
            int op_code = (Integer.parseInt(code,2) & 15);

            switch (op_type){

                case OPPILA: switch (op_code){
                                    case APILA: return "apila";
                                    case APILA_DIR: return "apila_dir";
                                    case DESAPILA_DIR: return "desapila_dir";
                                    default: return code;

                                }

                case OPARIT: switch (op_code) {
                                    case SUMA: return "suma";
                                    case RESTA: return "resta";
                                    case MULT: return "mult";
                                    case DIV: return "div";
                                    case MOD: return "mod";
                                    case DESPD: return "despD";
                                    case DESPI: return "despI";
                                    case SIGNO: return "cSigno";
                                    case ABS: return "abs";
                                    default: return code;

                                 }

                case OPLOG:  switch(op_code){
                                case AND: return "and";
                                case OR: return "or";
                                case MENOR: return "menor";
                                case MAYOR: return "mayor";
                                case MENIG: return "menor_igual";
                                case MAYIG: return "mayor_igual";
                                case DIST: return "distinto";
                                case IGUAL: return "igual";
                                default: return code;
                                }

                case OPMISC: switch(op_code){
                                case CFLOAT: return "(float)";
                                case CINT: return "(int)";
                                case CNAT: return "(natural)";
                                case CCHAR: return "(char)";
                                case LEER: return "leer";
                                case ESCR: return "escribir";
                                case STOP: return "STOP";
                                default:   return code;
                                }


            }
            return code;

        }

        public Instruction(String code, String field){
            this.code = code;
            this.field = field;
        }

        @Override
        public String toString(){

            if (field != null)
                return (translation(this.code) +" "+ this.field);
            else return (translation(this.code));
        }


    }

    private enum Ttipo {FLOAT,NAT,INT,CHAR,BOOLEAN};

    private class DataCell {

        Object data;
        Ttipo tipo;
       
        private DataCell(Object data, Ttipo tipo) {
            this.data=data;
            this.tipo =tipo;
        }

        @Override
        public String toString(){
            return "( " + data.toString() +" )";

        }

        public void print(){
                 System.out.print("("+data.toString()+")");
        }


    }



    private class StackObj{

        private Object data;
        private Ttipo tipo;

        public StackObj(Object data,Ttipo tipo){
            this.data = data;
            this.tipo = tipo;
        }

        public void setTipo(Ttipo tipo){
            this.tipo=tipo;
        }

        public void setData(Object data){
            this.data = data;
        }

        @Override
        public String toString(){

            return data.toString();



        }

    }

    //Data structures:

    private  LinkedList pila;
    private  HashMap memM;
    private  ArrayList prog;
    private  boolean parada;
    private  int pc;
    private  boolean traza;


    /*
     * Devuelve el tipo de la expresion recibida por parametro, para almacenarlo
     * junto con el valor.
     */
    private StackObj expType(String exp)throws ReadException{
        if(exp.matches("\\'.\\'")){
            //char
            return new StackObj(new Character(exp.charAt(1)),Ttipo.CHAR);
        }else if(exp.matches("\\-?\\d*(\\.|e|E).*")){
            //real sin exponente
            return new StackObj(new Float(exp),Ttipo.FLOAT);
        }else if(exp.matches("\\-\\d*")){
            //entero
            return new StackObj(new Integer(exp),Ttipo.INT);
        }else if(exp.matches("\\+\\d*")){
            //entero con signo "+" hay que eliminarlo antes de cambiarlo
            return new StackObj(new Integer(exp.substring(1)),Ttipo.INT);
        }else if(exp.matches("\\d*")){
            //natural
            return new StackObj(new Integer(exp),Ttipo.NAT);
        }else if(exp.matches("true|false")){
            return new StackObj(new Boolean(exp),Ttipo.BOOLEAN);
        }else throw new ReadException();

    }


    private void operacionPila(String code, String field) throws OperationException{

        //mascara "001111"
        switch (Integer.parseInt(code,2) & 15) {

            case APILA : try{
                            pila.addFirst(expType(field)) ;
                         }catch(Exception ex){
                             System.out.println(ex.getMessage());
                         }
                         break;

            case APILA_DIR :
                        DataCell dc = (DataCell)memM.get(field);
                        pila.addFirst(new StackObj(dc.data,dc.tipo) );
                        break;


            case DESAPILA_DIR :
                        StackObj sO= (StackObj)pila.removeFirst();
                        memM.put(field,new DataCell(sO.data,sO.tipo));
                        break;

             default: throw new OpNotSupportedException(code);

        }
    }

    private void castES(String code, String field) throws OperationException{

      
        //applied mask "001111"
        switch (Integer.parseInt(code, 2) & 15){

            case CFLOAT: {
                       StackObj sO=(StackObj)pila.getFirst();
                       float value;
                       if(sO.tipo == Ttipo.INT || sO.tipo==Ttipo.NAT){
                           value = ((Integer)sO.data);
                           sO.tipo=Ttipo.FLOAT;
                           sO.data=new Float(value);
                       }else if(sO.tipo == Ttipo.CHAR){
                           value = (float)(Character)sO.data;
                           sO.tipo=Ttipo.FLOAT;
                           sO.data=value;
                       }
                       }break;
            
            case CINT: {
                       StackObj sO=(StackObj)pila.getFirst();
                       int value;
                       if(sO.tipo == Ttipo.FLOAT){
                           value = ((Float)sO.data).intValue();
                           sO.tipo=Ttipo.INT;
                           sO.data=new Integer(value);
                       }else if(sO.tipo ==Ttipo.NAT)
                           sO.tipo=Ttipo.INT;
                       else if(sO.tipo == Ttipo.CHAR){
                           value = (int)(Character)sO.data;
                           sO.tipo=Ttipo.INT;
                           sO.data=value;
                       }
                       }break;
                
            
            case CNAT: {
                       StackObj sO=(StackObj)pila.getFirst();
                       int value;
                       value = (int)(Character)sO.data;
                       sO.tipo=Ttipo.NAT;
                       sO.data=value;

            
            
                        }
                break;
            case CCHAR:{
                       StackObj sO=(StackObj)pila.getFirst();
                       char value;
                       value = (char)((Integer)sO.data).intValue();
                       sO.tipo=Ttipo.CHAR;
                       sO.data=value;

                        }
                break;

            case LEER: leer() ;break;

            case ESCR: escribir();break;
                
            case STOP: parada = true; break;

            default: throw new OpNotSupportedException(code);

        }

    }

    private void leer(){
        boolean again = true;
        StackObj sO;
        while(again) {
            System.out.println("input:");
            try {
                String str = (new BufferedReader(new InputStreamReader(System.in))).readLine();
                sO= expType(str);
                again=false;
                //introducir en la pila el elemento leido
                pila.addFirst(sO);
            } catch (Exception ex) {
                System.out.println(ex.getMessage());
            }
        }

    }

    private void escribir(){
        StackObj sO =(StackObj) pila.getFirst();
        switch (sO.tipo){
            case INT:
                System.out.println((Integer)sO.data);break;
            case NAT:
                System.out.println((Integer)sO.data);break;
            case FLOAT:
                System.out.println((Float)sO.data);break;
            case CHAR:
                System.out.println((Character)sO.data);break;
            case BOOLEAN:
                System.out.println((Boolean)sO.data);break;
        }
    }
    /*
     * resultType
     * calcula el tipo de operacion aritmetica en funcion de los tipos de los operadores
     */

    private Ttipo resultType(Ttipo oper1, Ttipo oper2){
            if(oper1 == Ttipo.FLOAT || oper2 ==Ttipo.FLOAT){
                return Ttipo.FLOAT;
            }else if (oper1 == Ttipo.INT || oper2 ==Ttipo.INT)
                return Ttipo.INT;
            else return Ttipo.NAT;
    }

    private Ttipo resultType(Ttipo oper){
            if(oper == Ttipo.FLOAT) return Ttipo.FLOAT;
            else return Ttipo.INT;

    }

    private void operacionArit(String code) throws OperationException {

        StackObj op1,total;
        StackObj op2 = (StackObj)pila.removeFirst();
        try{
        //Mascara de bits "001111"

        switch (Integer.parseInt(code,2) & 15){

            case SUMA:
                op1 = (StackObj)pila.removeFirst();
                pila.addFirst( suma(op1,op2));
                break;

            case RESTA:
                op1 = (StackObj)pila.removeFirst();
                pila.addFirst( resta(op1,op2));
                break;

            case MULT:
                op1 = (StackObj)pila.removeFirst();
                pila.addFirst( mul(op1,op2));
                break;

            case DIV:
                op1 = (StackObj)pila.removeFirst();
                pila.addFirst( div(op1,op2));
                break;
                

            case MOD:
                op1 = (StackObj)pila.removeFirst();
                pila.addFirst( mod(op1,op2));
                break;


            case DESPD:
                op1 = (StackObj)pila.removeFirst();
                pila.addFirst( despD(op1,op2));
                break;


            case DESPI:
                op1 = (StackObj)pila.removeFirst();
                pila.addFirst( despI(op1,op2));
                break;

            case SIGNO:
                pila.addFirst( signo(op2));
                break;
            case ABS:
                pila.addFirst( absoluto(op2));
                break;

            default: throw new OpNotSupportedException(code);


        }


        }catch(Exception ex){
            System.out.println(ex.getMessage());
          }


    
        
        

    }

    private StackObj suma(StackObj op1,StackObj op2){
        Ttipo result=resultType(op1.tipo,op2.tipo);
        switch(result){

            case FLOAT: if(op1.tipo ==Ttipo.INT || op1.tipo==Ttipo.NAT)
                            return new StackObj(new Float((Integer)op1.data + (Float)op2.data),result);
                        else if(op2.tipo ==Ttipo.INT || op2.tipo==Ttipo.NAT)
                            return new StackObj(new Float((Float)op1.data + (Integer)op2.data),result);
                        else
                            return new StackObj(new Float((Float)op1.data + (Float)op2.data),result);

            case INT: return new StackObj(new Integer((Integer)op1.data + (Integer)op2.data),result);

            case NAT: return new StackObj(new Integer((Integer)op1.data + (Integer)op2.data),result);
        }
        return null;
    }

    private StackObj resta(StackObj op1,StackObj op2) throws OperationException{
        Ttipo result=resultType(op1.tipo,op2.tipo);
        switch(result){

            case FLOAT: if(op1.tipo ==Ttipo.INT || op1.tipo==Ttipo.NAT)
                            return new StackObj(new Float((Integer)op1.data - (Float)op2.data),result);
                        else if(op2.tipo ==Ttipo.INT || op2.tipo==Ttipo.NAT)
                            return new StackObj(new Float((Float)op1.data - (Integer)op2.data),result);
                        else
                            return new StackObj(new Float((Float)op1.data - (Float)op2.data),result);
                        


            case INT: return new StackObj(new Integer((Integer)op1.data - (Integer)op2.data),result);

            case NAT: Integer cal = new Integer((Integer)op1.data - (Integer)op2.data);
                      if(cal<0) throw new OperationException("resultado negativo, no soportado por tipo natural");
                      else return new StackObj(new Integer((Integer)op1.data - (Integer)op2.data),result);
        }
        return null;
    }

    private StackObj mul(StackObj op1,StackObj op2){
        Ttipo result=resultType(op1.tipo,op2.tipo);
        switch(result){

            case FLOAT: if(op1.tipo ==Ttipo.INT || op1.tipo==Ttipo.NAT)
                            return new StackObj(new Float((Integer)op1.data * (Float)op2.data),result);
                        else if(op2.tipo ==Ttipo.INT || op2.tipo==Ttipo.NAT)
                            return new StackObj(new Float((Float)op1.data * (Integer)op2.data),result);
                        else
                            return new StackObj(new Float((Float)op1.data * (Float)op2.data),result);

            case INT: return new StackObj(new Integer((Integer)op1.data * (Integer)op2.data),result);

            case NAT: return new StackObj(new Integer((Integer)op1.data * (Integer)op2.data),result);
            
        }
        return null;
    }

    private void no_cero(StackObj op2) throws ZeroException{
        
        if(op2.tipo == Ttipo.FLOAT){
            if ((Float)op2.data == 0) throw new ZeroException();
        }else
            if ((Integer)op2.data == 0) throw new ZeroException();             

    }

    private StackObj div(StackObj op1,StackObj op2) throws ZeroException{

        no_cero(op2);
        Ttipo result=resultType(op1.tipo,op2.tipo);
        //Java identifica el tipo de division (entera, real) segun los tipos de los operandos:
        switch(result){
            case FLOAT: if(op1.tipo ==Ttipo.INT || op1.tipo==Ttipo.NAT)
                            return new StackObj(new Float((Integer)op1.data / (Float)op2.data),result);
                        else if(op2.tipo ==Ttipo.INT || op2.tipo==Ttipo.NAT)
                            return new StackObj(new Float((Float)op1.data / (Integer)op2.data),result);
                        else
                            return new StackObj(new Float((Float)op1.data / (Float)op2.data),result);

            case INT: return new StackObj(new Integer((Integer)op1.data / (Integer)op2.data),result);

            case NAT: return new StackObj(new Integer((Integer)op1.data / (Integer)op2.data),result);
        }
        return null;
    }

    private StackObj mod(StackObj op1,StackObj op2) throws ZeroException{
        no_cero(op2);
        Ttipo result=op1.tipo;
        switch(result){

            case INT: return new StackObj(new Integer((Integer)op1.data % (Integer)op2.data),result);

            case NAT: return new StackObj(new Integer((Integer)op1.data % (Integer)op2.data),result);
        }
        return null;
    }

    private StackObj despI(StackObj op1, StackObj op2){
        return new StackObj(new Integer( (Integer)op1.data << (Integer)op2.data),Ttipo.NAT);
    }

    private StackObj despD(StackObj op1, StackObj op2){
        return new StackObj(new Integer( (Integer)op1.data >> (Integer)op2.data),Ttipo.NAT);
    }

    private StackObj signo(StackObj op1){
        Ttipo result=resultType(op1.tipo);
        switch(result){

            case FLOAT: return new StackObj(new Float(-(Float)op1.data),result);

            //si el tipo es natural tambien se opera en este case,
            //asignado tipo entero al resultado:
            case INT: return new StackObj(new Integer(-(Integer)op1.data),result);
        }
        return null;

    }

    private StackObj absoluto(StackObj op1){
        Ttipo result=resultType(op1.tipo);
        switch(op1.tipo){

            case FLOAT: return new StackObj(new Float(Math.abs((Float)op1.data)),result);

            //si el tipo es natural tambien se opera en este case,
            //asignado tipo entero al resultado:
            case NAT:
            case INT: return new StackObj(new Integer(Math.abs((Integer)op1.data)),Ttipo.NAT);
        }
        return null;

    }


    private void operacionLogi(String code) throws OperationException {

        StackObj op1,total;
        StackObj op2 = (StackObj)pila.removeFirst();

        //Mascara de bits "001111"
        switch (Integer.parseInt(code,2) & 15){
            case AND:
                    op1 = (StackObj)pila.removeFirst();
                    pila.addFirst(and(op1,op2));
                    break;

            case OR:
                    op1 = (StackObj)pila.removeFirst();
                    pila.addFirst(or(op1,op2));
                    break;

            case MENOR: 
                    op1 = (StackObj)pila.removeFirst();
                    pila.addFirst(menor(op1,op2));
                    break;

            case MAYOR:
                    op1 = (StackObj)pila.removeFirst();
                    pila.addFirst(mayor(op1,op2));
                    break;

            case MENIG:
                    op1 = (StackObj)pila.removeFirst();
                    pila.addFirst(menig(op1,op2));
                    break;

            case MAYIG:
                    op1 = (StackObj)pila.removeFirst();
                    pila.addFirst(mayig(op1,op2));
                    break;

            case DIST:
                    op1 = (StackObj)pila.removeFirst();
                    pila.addFirst(dist(op1,op2));
                    break;

            case IGUAL:
                    op1 = (StackObj)pila.removeFirst();
                    pila.addFirst(igual(op1,op2));
                    break;

            case NEG:
                    pila.addFirst(neg(op2));
                    break;

            default:  throw new OpNotSupportedException(code);
        }


    }

    private StackObj and(StackObj op1,StackObj op2){

        return new StackObj((Boolean)op1.data && (Boolean)op2.data,Ttipo.BOOLEAN);

    }

    private StackObj or(StackObj op1,StackObj op2){
        return new StackObj((Boolean)op1.data || (Boolean)op2.data,Ttipo.BOOLEAN);

    }

    private int bool_to_int(Boolean b){
        if (b.booleanValue() == true) return 1;
        else return 0;

    }

    private StackObj menor(StackObj op1,StackObj op2){
        switch(op1.tipo){
            case BOOLEAN :
                return new StackObj(new Boolean (bool_to_int((Boolean)op1.data) < bool_to_int((Boolean)op2.data)),Ttipo.BOOLEAN);
            case CHAR :
                return new StackObj(new Boolean ((int)(Character)op1.data < (int)(Character)op2.data),Ttipo.BOOLEAN);

            case FLOAT: { switch(op2.tipo){
                            case FLOAT: return new StackObj(new Boolean((Float)op1.data < (Float)op2.data),Ttipo.BOOLEAN);
                            case NAT:
                            case INT: return new StackObj(new Boolean((Float)op1.data < (Integer)op2.data),Ttipo.BOOLEAN);
                            }
                }
            case NAT:
            case INT:{  switch(op2.tipo){
                            case FLOAT: return new StackObj(new Boolean((Integer)op1.data < (Float)op2.data),Ttipo.BOOLEAN);
                            case NAT:
                            case INT: return new StackObj(new Boolean((Integer)op1.data < (Integer)op2.data),Ttipo.BOOLEAN);
                            }


            }

            default: return null;

        }
    }

    private StackObj mayor(StackObj op1,StackObj op2){
      switch(op1.tipo){
            case BOOLEAN :
                return new StackObj(new Boolean (bool_to_int((Boolean)op1.data) > bool_to_int((Boolean)op2.data)),Ttipo.BOOLEAN);
            case CHAR :
                return new StackObj(new Boolean ((int)(Character)op1.data > (int)(Character)op2.data),Ttipo.BOOLEAN);

            case FLOAT: { switch(op2.tipo){
                            case FLOAT: return new StackObj(new Boolean((Float)op1.data > (Float)op2.data),Ttipo.BOOLEAN);
                            case NAT:
                            case INT: return new StackObj(new Boolean((Float)op1.data > (Integer)op2.data),Ttipo.BOOLEAN);
                            }
                }
            case NAT:
            case INT:{  switch(op2.tipo){
                            case FLOAT: return new StackObj(new Boolean((Integer)op1.data > (Float)op2.data),Ttipo.BOOLEAN);
                            case NAT:
                            case INT: return new StackObj(new Boolean((Integer)op1.data > (Integer)op2.data),Ttipo.BOOLEAN);
                            }


            }

            default: return null;

        }


    }

    private StackObj igual(StackObj op1,StackObj op2){
  switch(op1.tipo){
            case BOOLEAN :
                return new StackObj(new Boolean (bool_to_int((Boolean)op1.data) == bool_to_int((Boolean)op2.data)),Ttipo.BOOLEAN);
            case CHAR :
                return new StackObj(new Boolean ((int)(Character)op1.data == (int)(Character)op2.data),Ttipo.BOOLEAN);

            case FLOAT: { switch(op2.tipo){
                            case FLOAT: return new StackObj(new Boolean((Float)op1.data == (Float)op2.data),Ttipo.BOOLEAN);
                            case NAT:
                            case INT: return new StackObj(new Boolean((Float)op1.data == (float)(Integer)op2.data),Ttipo.BOOLEAN);
                            }
                }
            case NAT:
            case INT:{  switch(op2.tipo){
                            case FLOAT: return new StackObj(new Boolean((float)(Integer)op1.data == (Float)op2.data),Ttipo.BOOLEAN);
                            case NAT:
                            case INT: return new StackObj(new Boolean((Integer)op1.data == (Integer)op2.data),Ttipo.BOOLEAN);
                            }


            }

            default: return null;

        }

    }

    private StackObj dist(StackObj op1,StackObj op2){
  switch(op1.tipo){
            case BOOLEAN :
                return new StackObj(new Boolean (bool_to_int((Boolean)op1.data) != bool_to_int((Boolean)op2.data)),Ttipo.BOOLEAN);
            case CHAR :
                return new StackObj(new Boolean ((int)(Character)op1.data != (int)(Character)op2.data),Ttipo.BOOLEAN);

            case FLOAT: { switch(op2.tipo){
                            case FLOAT: return new StackObj(new Boolean((Float)op1.data != (Float)op2.data),Ttipo.BOOLEAN);
                            case NAT:
                            case INT: return new StackObj(new Boolean((Float)op1.data != (float)(Integer)op2.data),Ttipo.BOOLEAN);
                            }
                }
            case NAT:
            case INT:{  switch(op2.tipo){
                            case FLOAT: return new StackObj(new Boolean((float)(Integer)op1.data != (Float)op2.data),Ttipo.BOOLEAN);
                            case NAT:
                            case INT: return new StackObj(new Boolean((Integer)op1.data != (Integer)op2.data),Ttipo.BOOLEAN);
                            }


            }

            default: return null;

        }

    }


    private StackObj menig(StackObj op1,StackObj op2){
        switch(op1.tipo){
            case BOOLEAN :
                return new StackObj(new Boolean (bool_to_int((Boolean)op1.data) <= bool_to_int((Boolean)op2.data)),Ttipo.BOOLEAN);
            case CHAR :
                return new StackObj(new Boolean ((int)(Character)op1.data <= (int)(Character)op2.data),Ttipo.BOOLEAN);

            case FLOAT: { switch(op2.tipo){
                            case FLOAT: return new StackObj(new Boolean((Float)op1.data <= (Float)op2.data),Ttipo.BOOLEAN);
                            case NAT:
                            case INT: return new StackObj(new Boolean((Float)op1.data <= (float)(Integer)op2.data),Ttipo.BOOLEAN);
                            }
                }
            case NAT:
            case INT:{  switch(op2.tipo){
                            case FLOAT: return new StackObj(new Boolean((float)(Integer)op1.data <= (Float)op2.data),Ttipo.BOOLEAN);
                            case NAT:
                            case INT: return new StackObj(new Boolean((Integer)op1.data <= (Integer)op2.data),Ttipo.BOOLEAN);
                            }


            }

            default: return null;

        }

    }



    private StackObj mayig(StackObj op1,StackObj op2){
        switch(op1.tipo){
            case BOOLEAN :
                return new StackObj(new Boolean (bool_to_int((Boolean)op1.data) >= bool_to_int((Boolean)op2.data)),Ttipo.BOOLEAN);
            case CHAR :
                return new StackObj(new Boolean ((int)(Character)op1.data >= (int)(Character)op2.data),Ttipo.BOOLEAN);

            case FLOAT: { switch(op2.tipo){
                            case FLOAT: return new StackObj(new Boolean((Float)op1.data >= (Float)op2.data),Ttipo.BOOLEAN);
                            case NAT:
                            case INT: return new StackObj(new Boolean((Float)op1.data >= (Integer)op2.data),Ttipo.BOOLEAN);
                            }
                }
            case NAT:
            case INT:{  switch(op2.tipo){
                            case FLOAT: return new StackObj(new Boolean((Integer)op1.data >= (Float)op2.data),Ttipo.BOOLEAN);
                            case NAT:
                            case INT: return new StackObj(new Boolean((Integer)op1.data >= (Integer)op2.data),Ttipo.BOOLEAN);
                            }


            }

            default: return null;

        }

    }

    private StackObj neg(StackObj op1){
        return new StackObj(new Boolean(!((Boolean)op1.data).booleanValue()),Ttipo.BOOLEAN);
    }




    private void printStack(){
        ListIterator it = pila.listIterator();
        while (it.hasNext())
            System.out.println(" | " + (it.next()).toString()+  " | ");


    }

    private void printMemory(){

        if(!memM.isEmpty())
            System.out.println(memM);

    }

    /**
     * Shows the Virtual Machine State
     */

    private void state_wait(Instruction i){
    System.out.println("Program Stack:");
    printStack();
    System.out.println("Program Memory:");
    printMemory();
    System.out.println("Program counter: " + pc );
    System.out.println("Next instruction: " + i.toString());
    System.out.println("Press enter to continue");
        try {
            InputStreamReader readIn = new InputStreamReader(System.in);
            readIn.read();
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }


    }


    public void inicia(){

        pila = new LinkedList();
        prog = new ArrayList();
        memM = new HashMap();
        parada = false;
        pc = 0;
        traza = false;
       

    }

    public void loadInstruction(String code, String field){
        prog.add(new Instruction(code,field));
    }

    public void loadInstruction(String code){
        loadInstruction(code,null);
    }

    public void inicia(boolean traza) {
        inicia();
        this.traza = traza;
    }

    public void run(){
        //Ejecucion del codigo
        while (!parada){
            Instruction i = (Instruction)prog.get(pc);
            if (traza)
                state_wait(i);
            pc++;
            executeInstruction(i);
        }
        System.out.println("Fin de programa");

    }

    public void executeInstruction(Instruction i){

      try{
        //Mascara de bits : "110000"
        int opCode = Integer.parseInt(i.code, 2) & 48;

        switch (opCode){

            //instruccion de fin
                //000000
            case 0 : operacionPila(i.code,i.field);break;
                //010000
            case 16 : operacionArit(i.code);break;
                //100000
            case 32: operacionLogi(i.code);break ;
                //110000
            case 48 : castES(i.code,i.field); break;

            default: throw new OpNotSupportedException(opCode);


        }

        }catch(Exception ex){
            System.out.println(ex.getMessage());
          } 
    }



}
